home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1994 / MacHack 1994.toast / MacHack™94 / Talks & Papers / Michael D. Crawford↵ / Word Services SDK 1.0.5 / Writeswell Jr. Source / DoChecking.c < prev    next >
Text File  |  1993-03-17  |  14KB  |  575 lines

  1. /* DoChecking.c
  2.  * Functions that actually do the calls to the Word Services servers.
  3.  * ©1992 Working Software, Inc.
  4.  * This source code is copyrighted.  Permission is granted to use the Word Services
  5.  * portion of the Writeswell Jr. source code in your own programs, but you 
  6.  * may not distribute the Writeswell Jr. word-processor code as a 
  7.  * commercial product.  If you modify the code, please do not call it 
  8.  * Writeswell Jr. (or Writeswell.)  This will ensure that people understand the 
  9.  * program and don’t have to deal with a number of different versions with 
  10.  * who-knows-what going on in the code.
  11.  * 
  12.  * Writeswell Jr. and Writeswell are trademarks of Working Software, Inc.
  13.  * 28 Dec 91 Mike Crawford
  14.  */
  15.  
  16. #include <Aliases.h>
  17. #include <Processes.h>
  18. #include <AppleEvents.h>
  19. #include <AEObjects.h>
  20. #include <AEPackObject.h>
  21. #include <AERegistry.h>
  22. #include "TBConstants.h"
  23. #include "TBGlobals.h"
  24. #include "WordServices.h"
  25. #include "AppEvents.h"
  26. #include "ObWind.h"
  27. #include "ObText.h"
  28. #include "Gripe.h"
  29. #include "Prefs.h"
  30. #include "DoChecking.h"
  31. #include "FindProcess.h"
  32. #include "TableCheck.h"
  33.  
  34. #define LAUNCH_BY_EVENT            /* Undef this to use Proc Mgr instead of Finder AE */
  35.  
  36. #define kLaunchTimeout    1800    /* If the speller's on a floppy, it may take a while */
  37. #define kBatchTimeout    600
  38.  
  39. OSErr LaunchSpeller( AliasHandle aliasHdl );
  40.  
  41. OSErr DoSpellCheck( short serviceNumber )
  42. {
  43.     OSErr            err;
  44.     AEDesc            docSpecifier;
  45.     AEDesc            textDescriptor;
  46.     AEDesc            textSpecifier;
  47.     AEAddressDesc    spellerAddr;
  48.     AppleEvent        btchEvent;
  49.     AppleEvent        replyEvent;
  50.     WWJrPrefsHdl    prefHdl;
  51.     ServiceType        servType;
  52.  
  53.     prefHdl = GetPrefHandle();
  54.     if ( !prefHdl ){
  55.         Gripe( "\pCannot get preferences handle" );
  56.         return resNotFound;
  57.     }
  58.  
  59.     err = OpenSpeller( serviceNumber, &spellerAddr, &servType );
  60.     if ( err ){
  61.         Gripe( "\pOpenSpeller failed to connect with speller" );
  62.         return err;
  63.     }
  64.  
  65.     switch ( servType ){
  66.         case kBatchService:
  67.             if ( (*prefHdl)->sendByList ){
  68.                 err = DoBatchCheck( &spellerAddr );
  69.                 if ( err ){
  70.                     Gripe( "\pDoBatchCheck failed" );
  71.                     return err;
  72.                 }
  73.             }else{
  74.                 err = DoBatchTableCheck( &spellerAddr );
  75.                 if ( err ){
  76.                     Gripe( "\pDoBatchTableCheck failed" );
  77.                     return err;
  78.                 }
  79.             }
  80.             break;
  81.         case kInteractiveService:
  82.             Gripe( "\pInteractive service is not implemented yet" );
  83.             return noErr;
  84.             break;
  85.         default:
  86.             Gripe( "\pBad service type code in preferences file" );
  87.             return noErr;
  88.     }
  89.  
  90.     return noErr;
  91. }
  92.  
  93. OSErr DoBatchCheck( AEAddressDesc *spellerAddrPtr )
  94. {
  95.     OSErr            err;
  96.     AEDesc            textSpecifier;
  97.     AppleEvent        btchEvent;
  98.     AppleEvent        replyEvent;
  99.     WWJrPrefsHdl    prefHdl;
  100.     AEDescList        textSpecList;            /* 1.0d7 */
  101.     long            index;                    /* 1.0d7 */
  102.  
  103.     /* We make an object specifier that refers to _our_own_ window
  104.      */
  105.  
  106.     err = CreateTextSpecifier( 1L, 1L, &textSpecifier );
  107.     
  108.     if ( err ){
  109.         Gripe( "\pCreateTextSpecifier failed" );
  110.         return err;
  111.     }
  112.  
  113.     prefHdl = GetPrefHandle();
  114.     if ( !prefHdl ){
  115.         Gripe( "\pCannot get preferences handle" );
  116.         return resNotFound;
  117.     }
  118.     
  119.     if ( (*prefHdl)->checkSel ){
  120.         /* Make a formRange descriptor that gives the selection range */
  121.  
  122.         Gripe( "\pSelection-only checking is not yet implemented" );
  123.         return noErr;
  124.     }
  125.  
  126.     /* Create the event to send to the speller */
  127.     
  128.     err = AECreateAppleEvent( kWordServicesClass,
  129.                                 kWSBatchCheckMe,
  130.                                 spellerAddrPtr,
  131.                                 kAutoGenerateReturnID,
  132.                                 kAnyTransactionID,
  133.                                 &btchEvent );
  134.     
  135.     if ( err ){
  136.         Gripe( "\pcreate btch event failed" );
  137.         return err;
  138.     }
  139.     err = AEDisposeDesc( spellerAddrPtr );
  140.     if ( err ){
  141.         Gripe( "\pAEDisposeDesc failed" );
  142.         return err;
  143.     }
  144.  
  145. #ifdef OLD_SPEC    
  146.  
  147.     /* This was used until 1.0d6 */
  148.  
  149.     /* Insert the object specifier as the direct object of the batch event */
  150.  
  151.     err = AEPutParamDesc( &btchEvent,
  152.                             keyDirectObject,
  153.                             &textSpecifier );
  154.     if ( err ){
  155.         Gripe( "\pAEPutParamDesc failed to put direct object on batch event" );
  156.         return err;
  157.     }
  158.     err = AEDisposeDesc( &textSpecifier );
  159.     if ( err ){
  160.         Gripe( "\pAEDisposeDesc failed" );
  161.         return err;
  162.     }
  163. #else
  164.  
  165.     /* 1.0d7 This is the new way to do it - use a list of object specifiers (even if
  166.      * only one of them
  167.      */
  168.     
  169.     err = AECreateList( (Ptr)NULL, (Size)0, false, &textSpecList );
  170.     if ( err ){
  171.         Gripe( "\pAECreateList failed" );
  172.         return err;
  173.     }
  174.     
  175.     /* Put our specifier into it.  If we had multiple text blocks to check, we would
  176.      * put specifiers for each into it in turn.
  177.      * Note that we use AEPutDesc, not AEPutParamDesc, to put items into an indexed
  178.      * list.
  179.      */
  180.  
  181. #define N_TEXT_SPECS    1L /*3L    */    /* Define this to be 1 for normal checking */
  182.  
  183.     for ( index = 1L; index <= N_TEXT_SPECS; index++ ){
  184.         err = AEPutDesc( &textSpecList,
  185.                             index,
  186.                             &textSpecifier );
  187.         if ( err ){
  188.             Gripe( "\pAEPutDesc failed to put text specifier into textSpecList" );
  189.             return err;
  190.         }
  191.     }
  192.  
  193.     err = AEDisposeDesc( &textSpecifier );
  194.     if ( err ){
  195.         Gripe( "\pAEDisposeDesc failed" );
  196.         return err;
  197.     }
  198.  
  199.     /* Insert the list of object specifiers as the direct object of the batch event */
  200.  
  201.     err = AEPutParamDesc( &btchEvent,
  202.                             keyDirectObject,
  203.                             &textSpecList );
  204.     if ( err ){
  205.         Gripe( "\pAEPutParamDesc failed to put direct object on batch event" );
  206.         return err;
  207.     }
  208.     
  209.     /* 1.0.3 MDC fixed a bug in which we disposed of textSpecifier a second
  210.      * time, instead of properly disposing of textSpecList
  211.      */
  212.  
  213.     err = AEDisposeDesc( &textSpecList );
  214.     if ( err ){
  215.         Gripe( "\pAEDisposeDesc failed" );
  216.         return err;
  217.     }
  218. #endif
  219.  
  220.     /* Send the event.  We await the reply, so that if there is a failure of some
  221.      * sort in the initial connection, we can alert the user right away.  The timeout
  222.      * value to use here should be as long as one would care to have a user wait for
  223.      * the completion of a menu command.  Since we expect that the speller is on a local
  224.      * machine in this case, and should be able to respond immediately, we just give
  225.      * a few seconds for the timeout.
  226.      *
  227.      * We should assign an idle proc to spin the cursor.  Even better would be a progress
  228.      * dialog that says "Contacting speller" or some such, with an animated display that
  229.      * shows the time elapsed relative to the total timeout, so the user will know how
  230.      * long she may have to wait
  231.      */
  232.     
  233.     err = AESend( &btchEvent,
  234.                     &replyEvent,
  235.                     kAEWaitReply + kAECanInteract + kAECanSwitchLayer,
  236.                     kAENormalPriority,
  237.                     kBatchTimeout,
  238.                     (IdleProcPtr)NULL,
  239.                     (EventFilterProcPtr)NULL );
  240.     
  241.     if ( err ){
  242.         Gripe( "\psend batch event failed" );
  243.         return err;
  244.     }
  245.     err = AEDisposeDesc( &btchEvent );
  246.     if ( err ){
  247.         Gripe( "\pAEDisposeDesc failed" );
  248.         return err;
  249.     }
  250.     
  251.      /* MDC 1.1.1 fix a memory leak */
  252.  
  253.     err = AEDisposeDesc( &replyEvent );
  254.     if ( err ){
  255.         Gripe( "\pAEDisposeDesc failed" );
  256.         return err;
  257.     }
  258.  
  259.     /* Now the event has been sent.  There is nothing more that we have to actually do
  260.      * on our own initiative to accomplish the spelling; we just sit back and respond
  261.      * to events.  In particular, we don't remember that spelling is taking place - once
  262.      * the spelling is done, we will just start seeing events from the user (mouse and
  263.      * key clicks and so on
  264.      */
  265.     
  266.     return noErr;
  267. }
  268.  
  269. OSErr OpenSpeller( short serviceNumber,
  270.                     AEAddressDesc *spellerAddrPtr,
  271.                     ServiceType *servTypePtr )
  272. {
  273.     WWJrPrefsHdl    prefHdl;
  274.     short            servID;
  275.     AliasHandle        aliasHdl;
  276.     short            curFile;
  277.     OSType            signature;
  278.     ProcessSerialNumber psn;
  279.     ProcessInfoRec        pInfo;
  280.     OSErr            err;
  281.     
  282.     prefHdl = GetPrefHandle();
  283.     if ( !prefHdl ){
  284.         Gripe( "\pCannot get prefs handle" );
  285.         return resNotFound;
  286.     }
  287.     
  288.     servID = gServItemID[ serviceNumber - 1 ];        /* C arrays start at 0 */
  289.     
  290.     *servTypePtr = (*prefHdl)->serviceType[ servID - kServiceBaseID ];
  291.  
  292.     curFile = CurResFile();
  293.     UseResFile( gPrefFileRefNum );
  294.     
  295.     aliasHdl = (AliasHandle)GetResource( rAliasType, servID );
  296.  
  297.     UseResFile( curFile );
  298.  
  299.     if ( !aliasHdl ){
  300.         Gripe( "\pCannot get alias handle for service" );
  301.         return resNotFound;
  302.     }
  303.     
  304.     /* See if the speller is out there */
  305.     signature = (*aliasHdl)->userType;
  306.  
  307.     if ( !FindAProcess( signature, &psn, &pInfo, (FSSpecPtr)NULL, (StringPtr)NULL ) ){
  308.  
  309.         EventRecord event;
  310.         short        i;
  311.  
  312.         err = LaunchSpeller( aliasHdl );
  313.         if ( err ){
  314.             Gripe( "\pUnable to launch Word Services server" );
  315.             return err;
  316.         }
  317. #ifdef NEVER        
  318.         /* Got to sleep for a little while - otherwise the speller croaks */
  319.         for ( i = 0; i < 10; i++ )
  320.             WaitNextEvent( 0, &event, 1, (RgnHandle)NULL );
  321. #endif
  322.     }    
  323.  
  324.     err = AECreateDesc( typeApplSignature,
  325.                         (Ptr)&signature,
  326.                         sizeof( signature ),
  327.                         spellerAddrPtr );
  328.     if ( err ){
  329.         Gripe( "\pAECreateDesc failed" );
  330.         return err;
  331.     }
  332.     
  333.     return noErr;
  334. }
  335.  
  336. #ifdef LAUNCH_BY_EVENT
  337. OSErr LaunchSpeller( AliasHandle aliasHdl )
  338. {
  339.     OSErr        err;
  340.     FSSpec        spellerSpec;
  341.     Boolean        changed;
  342.     AppleEvent        launchEvent;
  343.     AppleEvent        replyEvent;
  344.     AEDesc            aliasDesc;
  345.     AEDesc            folderDesc;
  346.     FSSpec            folderSpec;
  347.     AEDesc            finderAddr;
  348.     DescType        finderSig;
  349.     AliasHandle        fAliasHdl;
  350.     AEDescList        aliasList;
  351.  
  352.     /* Make sure the speller can still be found, and get its spec */
  353.     err = ResolveAlias( (FSSpecPtr)NULL, aliasHdl, &spellerSpec, &changed );
  354.     
  355.     if ( err ){
  356.         Gripe( "\pCannot locate speller" );        /* Might be user canceled AShare */
  357.         return err;
  358.     }
  359.     
  360.     if ( changed ){
  361.         ChangedResource( aliasHdl );
  362.         WriteResource( aliasHdl );
  363.     }
  364.     
  365.     /* make an alias for the parent folder */
  366.     
  367.     err = FSMakeFSSpec( spellerSpec.vRefNum,
  368.                         spellerSpec.parID,
  369.                         (StringPtr)NULL,
  370.                         &folderSpec );
  371.     if ( err ){
  372.         Gripe( "\pMakeFSSpec failed" );
  373.         return err;
  374.     }
  375.     
  376.     err = NewAlias( (FSSpecPtr)NULL, &folderSpec, &fAliasHdl );
  377.     if ( err ){
  378.         Gripe( "\pNewAlias failed" );
  379.         return err;
  380.     }
  381.  
  382.     /* Create the event to send to the Finder */
  383.     
  384.     finderSig = 'MACS';                        /* Creator code of finder; type is 'FNDR' */
  385.     err = AECreateDesc( typeApplSignature,
  386.                         (Ptr)&finderSig,
  387.                         sizeof( finderSig ),
  388.                         &finderAddr );
  389.     if ( err ){
  390.         Gripe( "\pAECreateDesc failed" );
  391.         return err;
  392.     }    
  393.                 
  394.     err = AECreateAppleEvent( kAEFinderEvents,
  395.                                 kAEOpenSelection,
  396.                                 &finderAddr,
  397.                                 kAutoGenerateReturnID,
  398.                                 kAnyTransactionID,
  399.                                 &launchEvent );
  400.     
  401.     if ( err ){
  402.         Gripe( "\pcreate open selection event failed" );
  403.         return err;
  404.     }
  405.     err = AEDisposeDesc( &finderAddr );
  406.     if ( err ){
  407.         Gripe( "\pAEDisposeDesc failed" );
  408.         return err;
  409.     }
  410.     
  411.     /* Insert the folder alias record as the direct object of the batch event */
  412.  
  413.     /* First make a descriptor of the alias */
  414.     HLock( fAliasHdl );
  415.  
  416.     err = AECreateDesc( typeAlias,
  417.                         (Ptr)*fAliasHdl,
  418.                         (*fAliasHdl)->aliasSize,
  419.                         &folderDesc );
  420.  
  421.     HUnlock( fAliasHdl );
  422.     DisposHandle( fAliasHdl );
  423.  
  424.     if ( err ){
  425.         Gripe( "\pAECreateDesc failed" );
  426.         return err;
  427.     }    
  428.     
  429.     err = AEPutParamDesc( &launchEvent,
  430.                             keyDirectObject,
  431.                             &folderDesc );
  432.     if ( err ){
  433.         Gripe( "\pAEPutParamDesc failed to put direct object on open selection event" );
  434.         return err;
  435.     }
  436.     err = AEDisposeDesc( &folderDesc );
  437.     if ( err ){
  438.         Gripe( "\pAEDisposeDesc failed" );
  439.         return err;
  440.     }
  441.  
  442.     /* Put the application alias on the event */
  443.     HLock( aliasHdl );
  444.  
  445.     err = AECreateDesc( typeAlias,
  446.                         (Ptr)*aliasHdl,
  447.                         (*aliasHdl)->aliasSize,
  448.                         &aliasDesc );
  449.  
  450.     HUnlock( aliasHdl );
  451.  
  452.     if ( err ){
  453.         Gripe( "\pAECreateDesc failed" );
  454.         return err;
  455.     }    
  456.  
  457.     /* Now make a list of the alias descriptor.  There is only one element in this
  458.      * list
  459.      */
  460.     
  461.     err = AECreateList( (Ptr)NULL, (Size)0, false, &aliasList );
  462.     
  463.     if ( err ){
  464.         Gripe( "\pAECreateList failed" );
  465.         return err;
  466.     }
  467.     
  468.     err = AEPutDesc( &aliasList, 1L, &aliasDesc );
  469.     
  470.     if ( err ){
  471.         Gripe( "\pAEPutDesc failed to put alias into alias list" );
  472.         return err;
  473.     }
  474.  
  475.     err = AEDisposeDesc( &aliasDesc );
  476.     if ( err ){
  477.         Gripe( "\pAEDisposeDesc failed" );
  478.         return err;
  479.     }
  480.     
  481.     err = AEPutParamDesc( &launchEvent,
  482.                             keySelection,
  483.                             &aliasList );
  484.     if ( err ){
  485.         Gripe( "\pAEPutParamDesc failed to put keySelection on open selection event" );
  486.         return err;
  487.     }
  488.  
  489.     err = AEDisposeDesc( &aliasList );
  490.     if ( err ){
  491.         Gripe( "\pAEDisposeDesc failed" );
  492.         return err;
  493.     }
  494.  
  495.     err = AESend( &launchEvent,
  496.                     &replyEvent,
  497.                     kAEWaitReply + kAECanInteract,        /* Don't allow later switch */
  498.                     kAENormalPriority,
  499.                     kLaunchTimeout,
  500.                     (IdleProcPtr)NULL,
  501.                     (EventFilterProcPtr)NULL );
  502.     
  503.     if ( err ){
  504.         Gripe( "\psend open selection event failed" );
  505.         return err;
  506.     }
  507.     err = AEDisposeDesc( &launchEvent );
  508.     if ( err ){
  509.         Gripe( "\pAEDisposeDesc failed" );
  510.         return err;
  511.     }
  512.     
  513.     err = AEDisposeDesc( &replyEvent );
  514.     if ( err ){
  515.         Gripe( "\pAEDisposeDesc failed" );
  516.         return err;
  517.     }
  518.     
  519.     return noErr;
  520. }
  521. #else
  522.  
  523. /* This makes the speller fail to process the batch event - I don't know why */
  524.  
  525. OSErr LaunchSpeller( AliasHandle aliasHdl )
  526. {
  527.     LaunchParamBlockRec    pb;
  528.     OSErr        err;
  529.     FSSpec        spellerSpec;
  530.     Boolean        changed;
  531.     char        *cPtr;
  532.     short        i;
  533.     
  534.     /* Make sure the speller can still be found */
  535.     err = ResolveAlias( (FSSpecPtr)NULL, aliasHdl, &spellerSpec, &changed );
  536.     
  537.     if ( err ){
  538.         Gripe( "\pCannot locate speller" );        /* Might be user canceled AShare */
  539.         return err;
  540.     }
  541.     
  542.     if ( changed ){
  543.         ChangedResource( aliasHdl );
  544.         WriteResource( aliasHdl );
  545.     }
  546.     
  547.     cPtr = (char*)&pb;
  548.     
  549.     for ( i = 0; i < sizeof( pb ); i++ ){
  550.         *cPtr++ = 0;
  551.     }
  552.  
  553.     pb.launchBlockID = extendedBlock;
  554.     pb.launchEPBLength = extendedBlockLen;
  555.     pb.launchControlFlags = launchNoFileFlags + launchContinue /*+ launchDontSwitch*/;
  556.     pb.launchAppSpec = &spellerSpec;
  557.     pb.launchAppParameters = NULL;
  558.     
  559.     err = LaunchApplication( &pb );
  560.     
  561.     if ( err == memFullErr ){
  562.     
  563.         Gripe( "\pThere is not enough memory to launch the Word Services server" );
  564.         return err;
  565.     }
  566.     
  567.     if ( err ){
  568.         Gripe( "\pUnable to launch the Word Services server" );
  569.         return err;
  570.     }
  571.  
  572.     return noErr;
  573. }
  574.  
  575. #endif /* LAUNCH_BY_EVENT */